package com.busi.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public abstract class CertificateCoder {

	private static final String PASSWORD = "123456";
	private static final String ALIAS = "www.ec.org";
	private static final String KEY_STORE_PATH = "/Users/mapeixu/Desktop/ec/ec.keystore";
	private static final String CERT_PATH = "/Users/mapeixu/Desktop/ec/ec.cer";
	private static final String CERT_TYPE = "X.509";
	private static final int MAX_ENCRYPT_BLOCK = 117;
	private static final int MAX_DECRYPT_BLOCK = 128;
	private static final Base64.Decoder decoder = Base64.getDecoder();
	private static final Base64.Encoder encoder = Base64.getEncoder();

	/**
	 * 私钥文件解密
	 * 
	 * @param in
	 * @return
	 */
	public static File decryptFile(File srcFileName, File destFileName) {
		OutputStream outputWriter = null;
		FileInputStream inputReader = null;
		try {
			// 取得私钥
			PrivateKey privateKey = getPrivateKey();
			// 对数据解密
			Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] buf = new byte[MAX_DECRYPT_BLOCK];
			int buf1;
			outputWriter = new FileOutputStream(destFileName);
			inputReader = new FileInputStream(srcFileName);
			while ((buf1 = inputReader.read(buf)) != -1) {
				byte[] encText = null;
				byte[] newArr = null;
				if (buf.length == buf1) {
					newArr = buf;
				} else {
					newArr = new byte[buf1];
					for (int i = 0; i < buf1; i++) {
						newArr[i] = (byte) buf[i];
					}
				}
				encText = cipher.doFinal(newArr);
				outputWriter.write(encText);
			}
			outputWriter.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (outputWriter != null) {
					outputWriter.close();
				}
				if (inputReader != null) {
					inputReader.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return destFileName;
	}

	/**
	 * 公钥文件加密
	 * 
	 * @param in
	 * @return
	 */
	public static File encryptFile(File srcFileName, File destFileName) {
		OutputStream outputWriter = null;
		FileInputStream inputReader = null;
		try {
			// 取得公钥
			PublicKey key = getPublicKey();
			// 对文件加密
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			byte[] buf = new byte[MAX_ENCRYPT_BLOCK];
			int buf1;
			outputWriter = new FileOutputStream(destFileName);
			inputReader = new FileInputStream(srcFileName);
			while ((buf1 = inputReader.read(buf)) != -1) {
				byte[] encText = null;
				byte[] newArr = null;
				if (buf.length == buf1) {
					newArr = buf;
				} else {
					newArr = new byte[buf1];
					for (int i = 0; i < buf1; i++) {
						newArr[i] = (byte) buf[i];
					}
				}
				encText = cipher.doFinal(newArr);
				outputWriter.write(encText);
			}
			outputWriter.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (outputWriter != null) {
					outputWriter.close();
				}
				if (inputReader != null) {
					inputReader.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return destFileName;
	}

	/**
	 * 签名验证
	 * 
	 * @param data
	 * @param sign
	 * @return
	 */
	public static boolean verify(byte[] data, String sign) {
		try {
			// 获得证书
			X509Certificate x509Certificate = (X509Certificate) getCertificate();
			// 由证书构建签名
			Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
			// 由证书初始化签名，实际上使用了证书中的公钥
			signature.initVerify(x509Certificate);
			signature.update(data);
			byte[] b = decoder.decode(sign);
			return signature.verify(b);
		} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 签名
	 * 
	 * @param sign
	 * @return
	 */
	public static String sign(byte[] sign) {
		try {
			// 获得证书
			X509Certificate certificate = (X509Certificate) getCertificateByStore();
			// 构建签名，由证书指定签名算法
			Signature signature = Signature.getInstance(certificate.getSigAlgName());
			// 获取私钥
			PrivateKey privateKey = getPrivateKey();
			// 初始化签名，由私钥构建
			signature.initSign(privateKey);
			signature.update(sign);
			String s = encoder.encodeToString(signature.sign());
			return s;
		} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 公钥解密
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] decryptByPublicKey(String data) {
		try {
			// 取得公钥
			PublicKey key = getPublicKey();
			// 对数据解密
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, key);
			byte[] b = decoder.decode(data);
			return cipher.doFinal(b);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 公钥加密(分段加密)
	 * 
	 * @param data
	 * @return
	 */
	public static String encryptByPublicKey(byte[] data) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			// 取得公钥
			PublicKey key = getPublicKey();
			// 对数据加密
			Cipher cipher = Cipher.getInstance(key.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			int len = data.length;
			int offSet = 0;
			byte[] cache;
			int i = 0; // 对数据分段加密
			while (len - offSet > 0) {
				if (len - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(data, offSet, len - offSet);
				}
				out.write(cache, 0, cache.length);
				out.flush();
				i++;
				offSet = i * MAX_ENCRYPT_BLOCK;
			}
			byte[] encryptedData = out.toByteArray();
			String encrypt = encoder.encodeToString(encryptedData);
			return encrypt;
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 私钥解密
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] decryptByPrivateKey(String data) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			// 取得私钥
			PrivateKey privateKey = getPrivateKey();
			// 对数据解密
			Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] b = decoder.decode(data);
			int len = b.length;
			int offSet = 0;
			byte[] cache;
			int i = 0;
			while (len - offSet > 0) {
				if (len - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(b, offSet, MAX_DECRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(b, offSet, len - offSet);
				}
				out.write(cache, 0, cache.length);
				out.flush();
				i++;
				offSet = i * MAX_DECRYPT_BLOCK;
			}
			byte[] encryptedData = out.toByteArray();
			return encryptedData;
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 私钥加密
	 * 
	 * @param data
	 * @return
	 */
	public static String encryptByPrivateKey(byte[] data) {
		try {
			// 取得私钥
			PrivateKey privateKey = getPrivateKey();
			// 对数据进行加密
			Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, privateKey);
			byte[] b = cipher.doFinal(data);
			String encrypt = encoder.encodeToString(b);
			return encrypt;
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取私钥
	 * 
	 * @return
	 */
	public static PrivateKey getPrivateKey() {
		try {
			// 获取秘钥库
			KeyStore ks = getKeyStore();
			// 获得私钥
			PrivateKey privateKey = (PrivateKey) ks.getKey(ALIAS, PASSWORD.toCharArray());
			return privateKey;
		} catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获得公钥
	 * 
	 * @return
	 */
	public static PublicKey getPublicKey() {
		// 获得证书
		Certificate certificate = getCertificate();
		// 获得公钥
		PublicKey key = certificate.getPublicKey();
		return key;
	}

	/**
	 * 由秘钥库获得数字证书构建数字签名对象
	 * 
	 * @return
	 */
	public static Signature getSignature() {
		try {
			// 获得秘钥库
			KeyStore ks = getKeyStore();
			// 获得证书
			X509Certificate certificate = (X509Certificate) ks.getCertificate(ALIAS);
			// 构建签名，由证书指定签名算法
			Signature signature = Signature.getInstance(certificate.getSigAlgName());
			return signature;
		} catch (KeyStoreException | NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获得keystore
	 *
	 * @return
	 */
	private static KeyStore getKeyStore() {
		KeyStore ks;
		FileInputStream is = null;
		try {
			// 实例化秘钥库
			ks = KeyStore.getInstance(KeyStore.getDefaultType());
			// 获得秘钥库文件流
			is = new FileInputStream(KEY_STORE_PATH);
			// 加载加密库
			ks.load(is, PASSWORD.toCharArray());
			// 关闭秘钥库文件流
			return ks;
		} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 加载数字证书
	 * 
	 * @return
	 */
	private static Certificate getCertificate() {
		FileInputStream in = null;
		try {
			// 实例化证书工厂
			CertificateFactory certificateFactory = CertificateFactory.getInstance(CERT_TYPE);
			// 取得证书文件流
			in = new FileInputStream(CERT_PATH);
			// 生成证书
			Certificate certificate = certificateFactory.generateCertificate(in);
			return certificate;
		} catch (CertificateException | FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 获取数字证书
	 * 
	 * @return
	 */
	private static Certificate getCertificateByStore() {
		try {
			// 获得秘钥库
			KeyStore keyStore = getKeyStore();
			// 获得证书
			Certificate certificate = keyStore.getCertificate(ALIAS);
			return certificate;
		} catch (KeyStoreException e) {
			e.printStackTrace();
		}
		return null;
	}
}
